import 'dart:async';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:share_fit/app/controllers/base/base_controller.dart';
import 'package:share_fit/app/controllers/home/home_const.dart';
import 'package:share_fit/app/controllers/home/weather/logic.dart';
import 'package:share_fit/app/controllers/space/space_const.dart';
import 'package:share_fit/app/helper/db/ute_db_helper.dart';
import 'package:share_fit/app/helper/ext/tr_ext.dart';
import 'package:share_fit/app/models/home/edit_widget_item.dart';
import 'package:share_fit/app/models/home/heart_rate_model.dart';
import 'package:share_fit/app/models/home/menstrual_mode.dart';
import 'package:share_fit/app/models/home/sleep_model.dart';
import 'package:share_fit/app/models/home/spo2_model.dart';
import 'package:share_fit/app/models/home/step_model.dart';
import 'package:share_fit/app/models/home/stress_model.dart';
import 'package:share_fit/app/routes/home_routes.dart';
import 'package:share_fit/app/tools/date_tool.dart';
import 'package:share_fit/app/tools/unit_util.dart';
import 'package:share_fit/app/ute/uteped_const.dart';
import 'package:share_fit/app/ute/uteped_device.dart';
import 'package:share_fit/generated/locales.g.dart';
import 'package:share_fit/pub/custom_refresh_indicator/custom_refresh_indicator.dart';
import 'home_state.dart';

class HomeLogic extends BaseController implements DeviceChangeListener {
  final HomeState state = HomeState();
  final IndicatorController refreshController = IndicatorController();

  _initTodaySummary(stepData) {
    int targetDistance = read("sport_target_distance", defaultValue: SpaceConst.targetDistances[0], global: true);
    int targetCalorie = read("sport_target_calorie", defaultValue: SpaceConst.targetCalories[0], global: true);
    int targetStep = read("sport_target_step", defaultValue: SpaceConst.targetSteps[0], global: true);

    TodaySummary summary = stepData!.summary;
    summary.setTarget(targetStep, targetDistance, targetCalorie, UnitUtil.getLengthUnit());
    state.todaySummary = summary;
    update(['todaySummary']);
  }

  Future<void> getTodaySummaryData() async {
    var db = await UteDbHelper.getDB();
    String date = DateTool.getYMDStr(DateTime.now());
    db.stepDao.findTodaySummary(getAddress(), date).then((stepData) => _initTodaySummary(stepData));
  }

  void getMenstrualSummaryData() {
    UteDbHelper.getDB().then((db) => //
        db.menstrualDao.findLatestOne(getAddress()).then((result) {
          if (result != null) {
            DateTime start = DateTime.fromMillisecondsSinceEpoch(result.menstruationStart!);
            DateTime end = DateUtils.addMonthsToMonthDate(start, 12);
            do {
              result.newModel(startDate: start);

              if (DateTool.somedayInMonth(start) || start.compareTo(end) > 0) {
                state.menstrualSummary = result;
                break;
              }

              start = DateTime.fromMillisecondsSinceEpoch(result.predictionStart!);
            } while (true);
          } else {
            state.menstrualSummary = result ?? MenstrualModel(false);
          }
          update([Type.cycleCalendar.code]);
        }));
  }

  void getHeartRateSummaryData() {
    UteDbHelper.getDB().then((db) => //
        db.heartRateDao.findLatestOne(getAddress(), DateTool.getYMDStr(DateTime.now())).then((result) {
          HeartRateModel model = result ?? HeartRateModel();
          state.hrSummary = HRSummary(heartRate: model.heartRateAvg ?? 0);
          update([Type.heartRate.code]);
        }));
  }

  Future<void> getHeartRateAvgData() async {
    final db = await UteDbHelper.getDB();
    List<HeartRateModel> rates = await db.heartRateDao.groupByHour(getAddress(), DateTool.getYMDStr(DateTime.now()));
    print('rate length===>:${rates.length}');
    List<HRSummary> heartRates = List.generate(24, (index) => HRSummary());
    for (int i = 0; i < rates.length; i++) {
      HeartRateModel element = rates[i];
      HRSummary hrs = HRSummary.from(element);
      hrs.heartRate = element.heartRateAvg ?? 0;
      int index = int.parse(element.calendar!.split(':')[0]);
      heartRates[index] = hrs;
      print('======>>> ${element.toMap()}');
    }

    HRSummary hrs = HRSummary();
    hrs.heartRates = heartRates;
    state.hrAvg = hrs;

    /// 计算心率小组件的指标值
    getHeartRateSummaryData();
    update([Type.heartRate.code]);
  }

  void getSleepSummaryData() {
    DateTime today = DateTime.now();
    DateTime temp = today.add(const Duration(days: -1));
    DateTime start = DateTime(temp.year, temp.month, temp.day, 20, 0, 0);
    DateTime end = DateTime(today.year, today.month, today.day, 20, 0, 0);
    UteDbHelper.getDB().then((db) => //
        db.sleepDao.findTodayData(getAddress(), DateTool.getYMDHMSStr(start), DateTool.getYMDHMSStr(end)).then((result) {
          int? sleepSum = 0;
          if (result.isNotEmpty) {
            sleepSum = result
                .where((element) => element.sleepType != 3) // 去掉睡眠类型为清醒的记录
                .map((e) => e.sleepTime)
                .reduce((value, element) => value! + element!);
          }
          SleepSummary ss = SleepSummary(sleepTimeSum: sleepSum ?? 0);
          ss.setTarget(target: read("target_sleep", defaultValue: 8, global: true));
          state.sleepSummary = ss;
          update([Type.sleep.code]);
        }));
  }

  Future<void> getStressSummaryData() async {
    UteDbHelper.getDB().then((db) => //
        db.stressDao.findLatestOne(getAddress(), DateTool.getYMDStr(DateTime.now())).then((value) {
          StressModel stressData = value ?? StressModel();
          state.stressSummary = stressData;
          update([Type.stress.code]);
        }));
  }

  Future<void> getSpo2SummaryData() async {
    UteDbHelper.getDB().then((db) => //
        db.spo2Dao.findLatestOne(getAddress(), DateTool.getYMDStr(DateTime.now())).then((result) {
          Spo2Model model = result ?? Spo2Model();
          state.spo2Summary = Spo2Summary(oxygenValue: model.oxygenValue ?? 0);
          update([Type.heartRate.code]);
        }));
  }

  Future<void> getSpo2AvgData() async {
    final db = await UteDbHelper.getDB();
    DateTime today = DateTime.now();
    List<Spo2Model> data = await db.spo2Dao.groupByHour(getAddress(), DateTool.getYMDStr(today));
    List<Spo2Summary> oxygenValues = List.generate(24, (index) => Spo2Summary());
    for (int i = 0; i < data.length; i++) {
      Spo2Model element = data[i];
      Spo2Summary spo2summary = Spo2Summary(calendar: element.calendar ?? '', oxygenValue: element.oxygenValue ?? 0);
      int index = int.parse(element.calendar!.split(':')[0]);
      oxygenValues[index] = spo2summary;
    }

    Spo2Summary ss = Spo2Summary();
    ss.oxygenAvgs = oxygenValues;
    state.spo2Avg = ss;

    /// 计算血氧小组件的指标值
    getHeartRateSummaryData();

    update([Type.bloodOxygen.code]);
  }

  Future<void> getWorkoutData() async {
    UteDbHelper.getDB().then(
      (db) => db.workoutDao.findNewestOne(getAddress(), DateTool.getYMDStr(DateTime.now())).then((list) {
        state.todayWorkout.workouts = list;
        update(['today_workout']);
      }),
    );
  }

  @override
  String getRemotePath() {
    return UTEPedConst.pathHome;
  }

  @override
  bool useMethodSupport() {
    return true;
  }

  @override
  List<String> getSupportedMethods() {
    return [UTEPedConst.methodLocalNotify];
  }

  @override
  void onRemoteEvent(event, method) {
    if (method == UTEPedConst.methodLocalNotify && event[UTEPedConst.keyAction] == UTEPedConst.actionSportDataNotify) {
      getTodaySummaryData();
      getWorkoutData();
    } else if (method == UTEPedConst.methodLocalNotify && event[UTEPedConst.keyAction] == UTEPedConst.actionSyncDataComplete) {
      _notifyLoadingComplete();
      refreshHomeData();
    } else if (event[UTEPedConst.keyStatus] == UTEPedConst.stateDisconnected) {
      print('hahah');
    }
  }

  @override
  void onDeviceChangeEvent(event) {
    String action = event[UTEPedConst.keyAction];
    if (action == UTEPedConst.actionDisconnect) {
      UteDbHelper.clearAllDataWith(event[UTEPedConst.keyAddress]);
      refreshHomeData(checkDeviceStatus: false);
    } else if (action == UTEPedConst.actionConnect) {
      syncWeatherData();
    }
  }

  String buildIndicatorValue(EditWidgetItem item) {
    if (item.title == LocaleKeys.widgets_heart_rate.ts) {
      return state.hrSummary.heartRateText;
    } else if (item.title == LocaleKeys.widgets_sleep.ts) {
      return state.sleepSummary.sleepSumText;
    } else if (item.title == LocaleKeys.widgets_stress.ts) {
      return state.stressSummary.stressText;
    } else if (item.title == LocaleKeys.widgets_blood_oxygen.ts) {
      return state.spo2Summary.spo2Text;
    } else if (item.title == LocaleKeys.widgets_cycle_calendar.ts) {
      return state.menstrualSummary.menstrualText;
    } else {
      return '';
    }
  }

  @override
  void onInit() {
    UTEPedDevice.instance.addListener(this);
    super.onInit();

    _addListeners();
  }

  @override
  String getStoragePath() {
    return UTEPedConst.pathDevice;
  }

  @override
  initState() {
    refreshHomeData(checkDeviceStatus: false);
  }

  void initMyWidget() {
    List hasListTemp = read(HomeConst.hasWidgetList, defaultValue: state.defaultWidgetList, global: true);
    state.hasList = EditWidgetItem.init().fromList(hasListTemp);
    // hiddenCycleCalendarWidget();
    update(['hasWidgetList']);
  }

  /// 主页中已有运动记录入口，在编辑完小组件后需要将“运动记录”小组件排除
  filterExistsWidget(from) {
    state.hasList = from;

    List hasListTemp = state.hasList.map((e) => e.toMap()).toList();
    save(HomeConst.hasWidgetList, hasListTemp, global: true);
    update(['hasWidgetList']);
  }

  goExerciseRecordsPage() {
    Get.toNamed(HomeRoutes.exerciseRecordsPage);
  }

  goEditWidgetPage(BuildContext context) {
    Get.toNamed(HomeRoutes.editWidgetPage, arguments: state.hasList)?.then((value) {
      if (value != null) {
        filterExistsWidget(value);
      }

      update(['hasWidgetList']);
    });
  }

  goWeatherPage() {
    Get.toNamed(HomeRoutes.weatherPage);
  }

  Future<void> refreshHomePage() async {
    var connected = await UTEPedDevice.instance.isConnected();
    if (!connected) {
      showTips(LocaleKeys.info.ts, LocaleKeys.device_not_connected.ts);
      return;
    }
    UTEPedDevice.instance.getUteConfig().syncData(action: UTEPedConst.actionRemoteRefresh);
  }

  Future<void> refreshHomeData({checkDeviceStatus=true}) async {
    var connected = await UTEPedDevice.instance.isConnected();
    if (!connected && checkDeviceStatus) {
      _notifyLoadingComplete();
      return;
    }
    getTodaySummaryData();
    getWorkoutData();
    getHeartRateAvgData();
    getSleepSummaryData();
    getStressSummaryData();
    getSpo2SummaryData();
    getSpo2AvgData();
    getMenstrualSummaryData();

    syncWeatherData();
  }

  void syncWeatherData() {
    WeatherLogic weatherLogic = Get.put(WeatherLogic());
    weatherLogic.fromCacheOrRemoteLoad();
  }

  void _notifyLoadingComplete() {
    if (refreshController.state.isIdle || refreshController.state.isComplete) {
      return;
    }
    refreshController.notifyComplete();
  }

  gotoWidgetPage(EditWidgetItem item) {
    if (item.title == LocaleKeys.widgets_cycle_calendar.ts) {
      UteDbHelper.getDB().then((db) => db.menstrualDao.findLatestOne(getAddress()).then((value) {
            if (value == null) {
              Get.toNamed(HomeRoutes.cycleCalendarSettingPage);
            } else {
              Get.toNamed(HomeRoutes.cycleCalendarPage)?.whenComplete(() => getMenstrualSummaryData());
            }
          }));
    } else {
      Get.toNamed(item.route ?? '');
    }
  }

  /// 根据性别隐藏生理周期小组件
  hiddenCycleCalendarWidget({int? gender}) {
    gender = gender ?? read('space_person_gender', defaultValue: -1, global: true);
    if (gender != 2) {
      state.hasList.removeWhere((element) => element.type.code == Type.cycleCalendar.code);
    } else {
      bool exists = state.hasList.any((element) => element.type.code == Type.cycleCalendar.code);
      state.hasList.addIf(
        !exists,
        EditWidgetItem(
            width: 1,
            height: 1,
            type: Type.cycleCalendar,
            action: 'remove',
            title: LocaleKeys.widgets_cycle_calendar.ts,
            image: 'assets/images/home/menstrual.png',
            route: HomeRoutes.cycleCalendarPage),
      );
    }
  }

  void _addListeners() {
    addStorageListener('space_person_gender', (value) {
      hiddenCycleCalendarWidget(gender: value);

      List hasListTemp = state.hasList.map((e) => e.toMap()).toList();
      save(HomeConst.hasWidgetList, hasListTemp, global: true);

      update(['hasWidgetList']);
    }, global: true);

    addStorageListener(SpaceConst.spaceUnitLength, (value) {
      refreshHomeData();
    }, global: true);
  }
}
